package com.example.demo2.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo2.mapper.HotelMapper;
import com.example.demo2.model.dtos.QueryDto;
import com.example.demo2.model.pojo.Hotel;
import com.example.demo2.model.pojo.HotelElastic;
import com.example.demo2.model.vos.GroupVo;
import com.example.demo2.model.vos.HotelPageVo;
import com.example.demo2.service.HotelService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements HotelService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public HotelPageVo search(QueryDto queryDto) throws IOException {
        HotelPageVo hotelPageVo = new HotelPageVo();

        //查询数据
        SearchRequest searchReq = new SearchRequest("hotel");

        SearchSourceBuilder sourceBuilder = searchReq.source();
        //构建查询条件（多个条件--bool，动态条件-判断）
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //希望根据用户搜索条件，进行匹配度由高到低的一个返回--算分
        List<QueryBuilder> must = boolQueryBuilder.must();

        //搜索关键字条件
        String keyword = queryDto.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            must.add(QueryBuilders.matchQuery("all",keyword));
        }

        //城市条件
        List<String> cityNames = queryDto.getCityNames();
        if (cityNames !=null && cityNames.size()>0){
            /*BoolQueryBuilder shoudBoolQuery = QueryBuilders.boolQuery();
            List<QueryBuilder> should = shoudBoolQuery.should();
            cityNames.forEach(cityName->{
                should.add(QueryBuilders.termQuery("city",cityName));
            });
            must.add(shoudBoolQuery);*/

            //一个字段匹配多个值，多个值之间是一个or的关系
            must.add(QueryBuilders.termsQuery("city",cityNames.toArray(new String[0])));
        }
        
        //星级条件
        List<String> starNames = queryDto.getStarNames();
        if (starNames !=null && starNames.size()>0){
            must.add(QueryBuilders.termsQuery("starName",starNames.toArray(new String[0])));
        }
        
        //品牌条件
        List<String> brandNames = queryDto.getBrandNames();
        if (brandNames !=null && brandNames.size()>0){
            must.add(QueryBuilders.termsQuery("brand",brandNames.toArray(new String[0])));
        }

        sourceBuilder.query(boolQueryBuilder);

        //分页条件
        int from = (queryDto.getPageNum()-1)* queryDto.getSize();
        sourceBuilder.from(from);
        sourceBuilder.size(queryDto.getSize());

        //按城市名称分组聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("cityTerms").field("city"));
        //按星级分组聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("starTerms").field("starName"));
        //按品牌分组聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandTerms").field("brand"));


        SearchResponse response = client.search(searchReq, RequestOptions.DEFAULT);

        SearchHits responseHits = response.getHits();
        //解析列表数据
        SearchHit[] hits = responseHits.getHits();
        List<HotelElastic> hotelElasticList = Stream.of(hits).map(hit -> {
            String json = hit.getSourceAsString();
            HotelElastic hotelElastic = JSON.parseObject(json, HotelElastic.class);
            return hotelElastic;
        }).collect(Collectors.toList());
        //向hotelPageVo中设置酒店信息数据
        hotelPageVo.setList(hotelElasticList);

        //解析总记录数
        long total = responseHits.getTotalHits().value;
        //向hotelPageVo中设置总记录数
        hotelPageVo.setTotal(total);

        //解析城市分组信息
        List<GroupVo> cityGroupVos = parseGroupVo(response,"cityTerms",GroupVo.GroupType.CITY);
        //设置城市分组信息
        hotelPageVo.setCityGroup(cityGroupVos);

        //解析星级分组信息
        List<GroupVo> starGroupVos = parseGroupVo(response,"starTerms",GroupVo.GroupType.STAR);
        //设置星级分组信息
        hotelPageVo.setStarGroup(starGroupVos);

        //解析品牌分组信息
        List<GroupVo> brandGroupVos = parseGroupVo(response,"brandTerms",GroupVo.GroupType.BRAND);
        //设置品牌分组信息
        hotelPageVo.setBrandGroup(brandGroupVos);

        return hotelPageVo;
    }

    private List<GroupVo> parseGroupVo(SearchResponse response, String aggName, GroupVo.GroupType type) {
        ParsedStringTerms cityTerms = response.getAggregations().get(aggName);
        List<? extends Terms.Bucket> buckets = cityTerms.getBuckets();
        List<GroupVo> groupVos = buckets.stream().map(bucket -> {
            String name = bucket.getKeyAsString();
            Long count = bucket.getDocCount();
            GroupVo groupVo = new GroupVo();
            //设置分组名称
            groupVo.setName(name);
            //设置分组数量
            groupVo.setCount(count.intValue());
            //设置分组类型
            groupVo.setType(type);
            return groupVo;
        }).collect(Collectors.toList());
        return groupVos;
    }
}
